package auth

import (
	"core/action"
	"core/coredto"
	"core/coreentity"
	"core/coreservice"
	"core/corestruct"
	"core/coreutil"
	"core/datetime"
	"core/myerrors"
	"core/redis"
	"fmt"

	"gorm.io/gorm"
	"strconv"
	"strings"
	"time"
)

const (
	TOKEN_PREV    = "token"
	MENU_PREV     = "menu"
	ROLEMENU_PREV = "rolemenu"
	HASURL_PREV   = "hasurl"
)

/**
*获取登录信息
 */
func GetSession(token string, fromCache bool, ctx *corestruct.HttpContext) (*corestruct.HttpSession, error) {
	var key = TOKEN_PREV + token
	var model = &corestruct.HttpSession{}
	if fromCache {
		var err = redis.RedisGetStruct(key, model)
		if err != nil {

		} else {
			return model, nil
		}
	}
	var tx = ctx.DB.Model(&model).Where("token=?", token).
		Where("online=?", 1).
		Find(model)
	if tx.Error != nil {
		return nil, tx.Error
	} else {
		if "" != model.HtroleData {
			var roles = make([]corestruct.IdNameDto, 0)
			var err = coreutil.Json.Unmarshal(model.HtroleData, &roles)
			if err != nil {
				return nil, err
			}
			model.Htroles = roles
		}
		redis.RedisSetStruct(key, model, 3600)

		return model, nil
	}
}
func DeleteSession(token string) {
	var key = TOKEN_PREV + token
	coreutil.RedisUtil.Del(key)
}

/**
*创建密码
 */
func CreatePwd(ename, pwd string) string {
	return coreutil.Md5Util.Md5(ename + pwd)
}

/**
*生产Token
 */
func CreateToken(id int64) string {
	return coreutil.IdUtil.UuidStr()
}

/**
*登录检查
 */
func LoginCheck(mm *coredto.LoginUserPwdDto, ctx *corestruct.HttpContext) (*coreentity.HtUser, *[]coredto.HtDeptBaseDto, error) {
	var muser = &coreentity.HtUser{}
	var ckpwd = CreatePwd(mm.Ename, mm.Pwd)
	var tx = ctx.DB.Model(&coreentity.HtUser{}).
		Where("ename=?", mm.Ename).
		Find(muser)
	if tx.Error != nil {
		return nil, nil, tx.Error
	}
	if tx.RowsAffected == 0 {
		return nil, nil, myerrors.New("用户不存在")
	}

	if ckpwd != muser.Pwd {
		return nil, nil, myerrors.New("密码错误")
	}
	var r, err = coreservice.HtUserDeptService.GetUserDepts(muser.Id, ctx)
	return muser, r, err
}

/**
*登录
 */
func Login(mm *coredto.LoginDto, ctx *corestruct.HttpContext) (string, *myerrors.MyError) {
	var muser, l, err = LoginCheck(&coredto.LoginUserPwdDto{Ename: mm.Ename,
		Pwd: mm.Pwd}, ctx)
	if err != nil {
		return "", myerrors.NewByErr(err)
	}
	if len(*l) == 0 {
		return "", myerrors.New("没有机构")
	}
	var mdept = &(*l)[0]
	if mm.HtdeptId > 0 {
		for _, v := range *l {
			if v.Id == mm.HtdeptId {
				*mdept = v
			}
		}
	}
	if 0 == mdept.Id {
		return "", myerrors.New("请登录机构")
	}
	lrole, err := coreservice.HtUserRoleservice.GetRoles([]int64{muser.Id}, mdept.Id, ctx)
	if err != nil {
		return "", nil
	}
	lprole := make([]corestruct.IdNameDto, 0)
	for _, v := range lrole {
		lprole = append(lprole, corestruct.IdNameDto{
			Id:   v.Id,
			Name: v.Name,
		})
	}

	var su = &corestruct.HttpSession{
		Id:         muser.Id,
		Token:      CreateToken(muser.Id),
		Name:       muser.Name,
		HtdeptId:   mdept.Id,
		HtdeptName: mdept.Name,
		HtroleData: "",
		Htroles:    lprole,
		Loginway:   mm.Loginway,
		HtplatId:   mdept.HtplatId,
		CrtUid:     muser.Id,
		Debug:      false,
	}
	ctx.Session = su

	var mHtUserLogin = &coreentity.HtUserLogin{
		Id:         coreutil.IdUtil.GetId(),
		Token:      su.Token,
		Name:       su.Name,
		HtdeptId:   su.HtdeptId,
		HtdeptName: su.HtdeptName,
		HtroleData: su.HtroleData,
		Htroles:    su.Htroles,
		HtplatId:   su.HtplatId,
		Loginway:   su.Loginway,
		Extra:      "",
		Ip:         ctx.GetIp(),
		Os:         ctx.GetOs(),
		Imsi:       "",
		Browser:    ctx.GetBrowser(),
		Appver:     "",

		EdtUrl:     ctx.GetUrl(),
		Online:     true,
		LogoutDt:   datetime.MyTime(time.Now()),
		LogoutMemo: "",
		Debug:      false,

		CrtUid: su.CrtUid,
		CrtDt:  datetime.MyTime(time.Now()),
		CrtDid: su.HtdeptId,
		EdtDt:  datetime.MyTime(time.Now()),
		EdtUid: su.CrtUid,
	}
	HtroleData, err := coreutil.Json.Marshal(su.Htroles)
	if err != nil {
		return "", nil
	}
	mHtUserLogin.HtroleData = HtroleData
	mHtUserLogin.Appver, _ = mm.Extra["appver"]
	mHtUserLogin.Imsi, _ = mm.Extra["imsi"]
	mHtUserLogin.Appver, _ = mm.Extra["appver"]

	err = ctx.DB.Transaction(func(tx *gorm.DB) error {

		result := tx.Create(mHtUserLogin)
		if result.Error != nil {
			return result.Error
		}
		result = tx.Model(&coreentity.HtUserLogin{}).
			Where("id!=?", mHtUserLogin.Id).
			Where("crt_uid=?", mHtUserLogin.CrtUid).
			Where("loginway=?", mHtUserLogin.Loginway).
			Where("online=?", true).
			Updates(map[string]interface{}{"online": false,
				"logout_dt":   datetime.MyTime(time.Now()),
				"logout_memo": fmt.Sprintf("被 %s 踢下线", mHtUserLogin.Ip)})

		if result.Error != nil {
			return result.Error
		}
		return nil
	})

	return su.Token, nil
}

/**
*注销
 */
func Logout(ctx *corestruct.HttpContext) (int64, *myerrors.MyError) {
	var su = ctx.Session
	var db = ctx.DB
	var key = TOKEN_PREV + su.Token

	tx := db.Model(&coreentity.HtUser{}).Where("id=?", su.Id).
		Where("online=?", 1).
		Update("online=?", 0).
		Update("logout_dt=?", datetime.Now()).
		Update("logout_memo=?", "注销")
	if tx.Error != nil {
		return 0, myerrors.NewByErr(tx.Error)
	}

	coreutil.RedisUtil.Del(key)
	return 1, nil
}

/**
*获取平台菜单
 */
func GetPlatMenu(plat_id int64, fromCache bool, ctx *corestruct.HttpContext) (*[]coredto.TreeViewNode, error) {
	var key = MENU_PREV + strconv.FormatInt(plat_id, 10)
	var model []coredto.TreeViewNode
	if fromCache {
		var err = redis.RedisGetStruct(key, model)
		if err != nil {

		} else {
			return &model, nil
		}
	}

	var svr = &platMenu{
		platId: plat_id,
		ctx:    ctx,
		lmenu:  nil,
		lpage:  nil,
		lauth:  nil,
	}
	r, err := svr.getMenu()
	if err != nil {
		return nil, err
	}
	redis.RedisSetStruct(key, model, 60)
	return r, nil
}

/**
*获取角色关联菜单id
 */
func GetRoleMenuIds(role_id int64, fromCache bool, ctx *corestruct.HttpContext) (*[]int64, error) {
	var key = ROLEMENU_PREV + strconv.FormatInt(role_id, 10)
	var model []int64
	if fromCache {
		var err = redis.RedisGetStruct(key, &model)
		if err != nil {

		} else {
			return &model, nil
		}
	}

	var db = ctx.DB
	tx := db.Model(&coreentity.HtRoleMenu{}).Where("htrole_id=?", role_id).
		Select("htmenu_id")
	var svr = &coreservice.ListService[int64]{}
	r, err := svr.List(ctx, tx)
	if err != nil {
		return nil, err
	}
	redis.RedisSetStruct(key, *r, 60)
	return r, nil
}

/**
*获取当前用户菜单
 */
func GetCurrUserMenu(ctx *corestruct.HttpContext) (*[]coredto.TreeViewNode, error) {
	var menu, err = GetPlatMenu(ctx.Session.HtplatId, true, ctx)
	if err != nil {
		return nil, myerrors.NewByErr(err)
	}

	var arr = make([][]int64, 0)
	var idcount = 0
	for i := 0; i < len(ctx.Session.Htroles); i++ {
		var ids1, err = GetRoleMenuIds(ctx.Session.Htroles[i].Id, true, ctx)
		if err != nil {
			return nil, myerrors.NewByErr(err)
		}
		idcount += len(*ids1)
		arr = append(arr, *ids1)
	}

	ids := make([]int64, idcount)
	idcount = 0
	for _, v := range arr {
		copy(ids[idcount:], v)
		idcount += len(v)
	}

	deanMenu(menu, nil, &ids)
	return menu, nil
}

func deanMenu(r *[]coredto.TreeViewNode, tn *coredto.TreeViewNode, ids *[]int64) {
	var arr = r
	if tn == nil {

	} else {
		if tn.Children == nil {
			return
		}
		arr = tn.Children
	}
	for i := len(*arr) - 1; i >= 0; i-- {
		bfound := false
		for j := 0; j < len(*ids); j++ {

			if (*arr)[i].Id == (*ids)[j] {
				bfound = true
				break
			}
		}
		if !bfound {
			*arr = (*arr)[0 : len(*arr)-1]
			continue
		}
		deanMenu(r, &((*arr)[i]), ids)
	}

}

// HasAction
// 当前用户是否有权限
// @param act  Action
// @param ctx  上下文
// return bool
func HasAction(act *action.Action, ctx *corestruct.HttpContext) (bool, error) {
	var su = ctx.Session
	if su.CrtUid == 1000 {
		return true, nil
	}
	if act.AuthType == action.AuthType_NONE {
		return true, nil
	}
	if act.AuthType == action.AuthType_Login {
		return su.Id > 0, nil
	}
	for _, v := range su.Htroles {
		var r, err = HasUrl(v.Id, act.FullUrl, true, ctx)
		if err != nil {
			return false, err
		}
		if r {
			return true, nil
		}
	}
	return false, nil
}
func HasUrl(role_id int64, url string, fromCache bool, ctx *corestruct.HttpContext) (bool, error) {
	var key = HASURL_PREV + strconv.FormatInt(role_id, 10)
	var r bool
	if fromCache {
		var err = redis.RedisGetStruct(key, &r)
		if err != nil {

		} else {
			return r, nil
		}
	}

	urls, err := GetRoleUrl(role_id, ctx)
	if err != nil {
		return false, err
	}
	for _, v := range *urls {
		if v == url {
			r = true
			break
		}
		if strings.HasSuffix(v, "*") {
			v2 := strings.Trim(v, "*")
			v2 = strings.Trim(v2, "\\")
			if strings.HasPrefix(url, v2) {
				r = true
				break
			}
		}
	}
	if !r {
		fmt.Println("abcd")
	}
	redis.RedisSetStruct(key, r, 60)
	if err != nil {
		return false, err
	}
	return r, nil
}
func GetRoleUrl(role_id int64, ctx *corestruct.HttpContext) (*[]string, error) {
	var db = ctx.DB
	var arr []string
	var tx = db.Model(&coreentity.HtRoleMenu{}).
		Joins(" inner join htmenu mu on HtRoleMenu.htrole_id=? and HtRoleMenu.htmenu_id=mu.id", role_id).
		Joins(" inner join htpage pg on mu.htpage_id=pg.id").
		Joins(" inner join htpageauth auth on pg.id=auth.htpage_id  ").
		Select("auth.urls").
		Find(&arr)
	if tx.Error != nil {
		return nil, tx.Error
	}
	var r = make([]string, 0)
	for _, v := range arr {
		if strings.Index(v, ",") > 0 {
			var arr2 = strings.Split(v, ",")
			for _, v2 := range arr2 {
				r = append(r, strings.TrimSpace(v2))
			}
		} else {
			r = append(r, v)
		}
	}
	return &r, nil
}
